using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Threading;
using System.ComponentModel;

namespace BassMati.Diagnostics 
{
	/// <summary>
	/// Implements a trace listener that can filter output from a specific thread.
	/// </summary>
	public class ThreadListener : System.Diagnostics.TraceListener
	{
		#region - Constructors -

		/// <summary>
		/// Default constructor.
		/// </summary>
		public ThreadListener ( )
		{ }

		/// <summary>
		/// Initializing constructor
		/// </summary>
		/// <param name="configuration"></param>
		public ThreadListener ( string configuration )
		{
			System.Diagnostics.Trace.WriteLine( "ThreadListener.ctor", "Enter" );
			this.Init( configuration );
			System.Diagnostics.Trace.WriteLine( "ThreadListener.ctor", "Exit" );
		}
		
		#endregion - Constructors -
		
		#region - Member Fields -

		protected bool disposed = false;

		/// <summary>
		/// Holds references to the trace forms.
		/// </summary>
		System.Collections.ArrayList traceForms = new System.Collections.ArrayList( 5 );
		
		/// <summary>
		/// Line terminator.
		/// </summary>
		protected string crlf = "\r\n";

		#endregion - Member Fields -

		#region - Init -

		/// <summary>
		/// Initializes the trace windows from the configuration string.
		/// </summary>
		/// <param name="configuration"></param>
		/// <remarks>
		/// The creation of windows has been delayed because the application startup may not 
		/// yet be complete when the trace listener configuration is read from the app.config 
		/// file.
		/// </remarks>
		/// 
		private void Init ( string configuration )
		{
			System.Diagnostics.Trace.WriteLine( "ThreadListener.Init", "Enter" );
			string traceEntryFormat = "Default";
			// Split the configuration string into individual settings.
			string [ ] settings = configuration.Split( ';' );
			foreach ( string setting in settings )
			{
				// Split the key value pair and make up the key name.
				string [ ] keyValuePair = setting.Split( '=' );
				string key = keyValuePair [ 0 ].Trim ( ).ToLower ( );
				if ( key == "thread" ) 
				{
					// Create a thread trace window.
					// TODO (usp) We could have stored a delegate to the write method instead.
					TraceForm traceForm = new TraceForm( keyValuePair [ 1 ].Trim ( ));
					this.traceForms.Add( traceForm );
					traceForm.TraceEntryFormat = traceEntryFormat;
					traceForm.Show ( );
				}
				else if ( key == "format" )
				{
					traceEntryFormat = keyValuePair[ 1 ].Trim( );
				}
				// TODO (usp) Add instructions for indentation char/max/width.
			}
			System.Diagnostics.Trace.WriteLine( "ThreadListener.Init", "Exit" );
		}

		#endregion - Application Init -

		#region - Write Overrides -

		/// <summary>
		/// Searches a trace form with a filter that matches the current thread name.
		/// </summary>
		/// <returns>The matching trace form or null.</returns>
		/// 
		protected TraceForm FindTargetForm ( )
		{
			string threadName = Thread.CurrentThread.Name;
			foreach ( TraceForm traceForm in this.traceForms )
			{
				if ( traceForm.threadNameFilter == threadName ) return traceForm;
			}
			return null;
		}

		/// <summary>
		/// Identifies the TraceForm and calls its Write method.
		/// </summary>
		/// <param name="message"></param>
		/// <param name="category"></param>
		public override void Write ( string message, string category )
		{
			TraceForm traceForm = FindTargetForm( );
			if ( traceForm != null )
			{
				if ( category.StartsWith( "Exit")) traceForm.IndentLevel -= 1;
				((ISynchronizeInvoke) traceForm).Invoke( new TraceForm.WriteDelegate( traceForm.Write ), new object [ ] { message, category } );
				if ( category.StartsWith( "Enter")) traceForm.IndentLevel += 1;
			}
		} 

		public override void Write ( object o, string category )
		{
			this.Write( o.ToString(), category );
		}

		public override void Write ( object o )
		{
			this.Write( o.ToString(), string.Empty );
		}

		public override void Write ( string message )
		{
			this.Write( message, string.Empty );
		}

		public override void WriteLine( object o )
		{
			this.Write ( o.ToString( ) + crlf, string.Empty );
		}

		public override void WriteLine( object o, string category )
		{
			this.Write ( o.ToString( ) + crlf, category );
		}

		public override void WriteLine ( string message )
		{
			this.Write ( message + crlf, string.Empty );
		}

		public override void WriteLine ( string message, string category )
		{
			this.Write ( message + crlf, category);
		}

		#endregion - Write Overrides -

		#region - Application Shutdown -

		public override void Close()
		{
			if ( ! this.disposed )
			{
				System.Diagnostics.Trace.WriteLine( "ThreadListener.Close", "Enter" );
				foreach ( TraceForm form in this.traceForms ) form.ForceClose( );
				this.traceForms.Clear( );
				base.Close ();
				System.Diagnostics.Trace.WriteLine( "ThreadListener.Close", "Exit" );
			}
		}

		protected override void Dispose ( bool disposing )
		{
			this.disposed = true;
			base.Dispose (disposing);
		}

		#endregion - Application Shutdown -
	}
}
